package com.cskaoyan.javase.oop2._1extends._8override;

/**
 * @description: 方法的覆盖
 * @author: wuguidong@cskaoyan.onaliyun.com
 **/

/**
 * 在父子类允不允许存在同名的成员方法呢? 允许
 * 这时创建子类对象用子类引用指向,然后用对象名点访问该同名的成员方法,结果显示出子类方法的结果
 *  那么这时,父类的同名方法为什么访问不到呢?是被覆盖了?还是被隐藏了?
 * 通过给在父类中写一个方法调用同名成员方法,仍然体现出子类的行为,说明方法在这里是被覆盖了(普通的手段不能访问到父类中同名方法了)
 *
 * 为什么方法会被覆盖?对象名调用方法,它们的访问机制是什么样的?仍然分成两个部分:
 *      1. 研究对象名点调用成员方法的调用范围
 *      2. 研究对象名点调用成员方法的调用结果
 *
 * 在继承中,存在父子类时,创建对象的方式有三种:
 *      1.创建父类对象,用父类引用接收
 *      2.创建子类对象,用子类引用接收
 *      3.创建子类对象,用父类引用接收
 *
 * 首先是对象名点调用成员方法的调用范围:
 *      访问范围仍然是编译概念,这一点和对象名点访问成员变量是没有区别的
 *      访问范围看引用的数据类型就可以了,如果引用是父类类型,那么它的访问范围只有父类
 * 而如果引用是子类类型,那么它的访问范围是子类 + 父类
 *      同样的,编译器会去父类中查找成员方法,直到找到Object,找不到就会编译报错
 *
 *
 * 接下来研究对象名点调用成员方法的调用结果,分别调用同名的test()方法:
 *      1.创建父类对象,用父类引用接收
 *          father test 体现出父类的方法行为
 *      2.创建子类对象,用子类引用接收
 *          son test 体现出子类的方法行为
 *      3.创建子类对象,用父类引用接收
 *          son test 体现出子类的方法行为
 * 以上总结,对象名点调用成员方法,它的调用结果是根据对象的实际类型来决定的
 * 这其实很好理解,访问范围是编译概念,所以只需要看引用的数据类型就够了
 * 但是方法的调用必须等到程序运行时期,才能确定它的结果(运行时期对象的具体类型就决定了方法的调用结果)
 *
 * 方法的覆盖: 因为方法的访问结果是根据对象的具体类型决定的,所以方法在调用时,就体现为"覆盖"而不是隐藏
 *
 * 注意事项:
 *      1.方法的覆盖不是物理意义上的覆盖,只是JVM调用方法的机制导致的覆盖,仍然可以通过super点去访问父类的同名成员方法(受访问权限限制)
 *      2.方法的覆盖有自身的语法,它是java中非常重要的一个概念
 *
 *方法覆盖(重写)的语法:
 * 在java中父子类中允许出现同名的方法,称之为子类重写了/覆盖了父类的方法
 * 检测方法覆盖的手段,在子类重写父类的方法上面加一个注解: @Override,如果注解和方法都不报错,那么是一个合法的方法重写,反之不合法
 *
 *
 * 普通成员方法的语法:
 *      [访问权限修饰符] 返回值类型 方法名(形参列表){
 *          //方法体
 *      }
 * 方法的重写 vs 方法的重载,它们区别和相同点:
 * 相同点:
 *      1.无论是方法的重写还是方法的重载一定要求方法名必须相同
 *      2.无论是方法的重写还是方法的重载它们都不关心方法体的书写
 *
 * 不同点:
 *      1.方法的重载和访问权限修饰符没有任何关系,但是方法的覆盖中子类重写方法的访问权限只能更宽松或者保持一致,不能更严格
 *      2.方法的重载和返回值类型没有任何关心,但是方法的覆盖中,子类重写的方法的返回值类型必须保持和父类方法的返回值类型兼容.(留给童鞋们自己测试)
 *     分别测试基本数据类型返回值类型和引用数据类型返回值类型,测试一下什么叫兼容
 *      3.方法的重载必须要求两个方法的形参列表不同,但是方法的重写必须要求形参列表完全一致
 *      4.方法重载必须在同类中,方法的重写必须在两个类中(父类和子类)
 *      5.方法重载可以有多个方法一起组成方法重载,但是方法的重就发生在两个之间
 *
 *
 */
public class Demo {
    public static void main(String[] args) {
        Son s = new Son();
        s.test();
        s.invokeFatherTest();
        s.invokeSuperTest();

        /*Father f = new Father();
        f.test();


        Son s = new Son();
        s.test();

        Father fs = new Son();
        fs.test();
        fs.testFather();
        //fs.testSon();*/
    }
}

class Father {
    public void test() {
        System.out.println("father test");
    }

    public void testFather() {
        System.out.println("father only");
    }

    public void invokeFatherTest() {
        this.test();
    }
}

class Son extends Father {
    //Method does not override method from its superclass
    @Override
    public void test() {
        System.out.println("father test");
    }

    public void testSon() {
        System.out.println("son only");
    }

    public void invokeSuperTest() {
        super.test();
    }

}